import hashlib
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk

class MD5CheckerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("MD5校验工具")
        self.root.geometry("600x400")
        
        # 设置主题风格
        self.style = ttk.Style()
        self.style.theme_use('clam')
        
        self.create_widgets()
    
    def create_widgets(self):
        """创建界面组件"""
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 文件选择部分
        file_frame = ttk.LabelFrame(main_frame, text="文件选择", padding="10")
        file_frame.pack(fill=tk.X, pady=5)
        
        self.file_path = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.file_path, width=50).pack(side=tk.LEFT, expand=True, fill=tk.X, padx=5)
        ttk.Button(file_frame, text="浏览...", command=self.browse_file).pack(side=tk.LEFT, padx=5)
        
        # MD5输入部分
        md5_frame = ttk.LabelFrame(main_frame, text="MD5校验", padding="10")
        md5_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(md5_frame, text="预期MD5值:").pack(anchor=tk.W)
        self.expected_md5 = tk.StringVar()
        ttk.Entry(md5_frame, textvariable=self.expected_md5, width=50).pack(fill=tk.X, pady=5)
        
        # 按钮部分
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill=tk.X, pady=10)
        
        ttk.Button(btn_frame, text="计算MD5", command=self.calculate_md5).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="验证文件", command=self.verify_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="保存MD5", command=self.save_md5).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="清除", command=self.clear_all).pack(side=tk.RIGHT, padx=5)
        
        # 结果显示部分
        result_frame = ttk.LabelFrame(main_frame, text="结果", padding="10")
        result_frame.pack(fill=tk.BOTH, expand=True)
        
        self.result_text = tk.Text(result_frame, height=8, wrap=tk.WORD)
        self.result_text.pack(fill=tk.BOTH, expand=True)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN).pack(fill=tk.X, pady=(5,0))
    
    def browse_file(self):
        """浏览文件"""
        file_path = filedialog.askopenfilename()
        if file_path:
            self.file_path.set(file_path)
            self.status_var.set(f"已选择文件: {os.path.basename(file_path)}")
    
    def calculate_md5(self, file_path=None):
        """计算文件的MD5哈希值"""
        if not file_path:
            file_path = self.file_path.get()
        
        if not file_path or not os.path.isfile(file_path):
            messagebox.showerror("错误", "请选择有效的文件路径")
            return None
        
        self.status_var.set("正在计算MD5...")
        self.root.update_idletasks()
        
        md5 = hashlib.md5()
        try:
            with open(file_path, 'rb') as f:
                for block in iter(lambda: f.read(65536), b''):
                    md5.update(block)
            result = md5.hexdigest()
            
            self.result_text.insert(tk.END, f"{file_path} 的MD5值:\n{result}\n\n")
            self.status_var.set(f"计算完成: {os.path.basename(file_path)}")
            return result
        except IOError as e:
            messagebox.showerror("错误", f"无法读取文件: {str(e)}")
            self.status_var.set("计算失败")
            return None
    
    def verify_file(self):
        """验证文件的MD5值"""
        file_path = self.file_path.get()
        expected_md5 = self.expected_md5.get().strip().lower()
        
        if not file_path or not os.path.isfile(file_path):
            messagebox.showerror("错误", "请选择有效的文件路径")
            return
        
        if not expected_md5 or len(expected_md5) != 32:
            messagebox.showerror("错误", "请输入有效的32位MD5值")
            return
        
        actual_md5 = self.calculate_md5(file_path)
        if actual_md5 is None:
            return
        
        if actual_md5 == expected_md5:
            self.result_text.insert(tk.END, "验证成功: MD5值匹配!\n\n")
            messagebox.showinfo("成功", "文件MD5验证成功!")
        else:
            self.result_text.insert(tk.END, f"验证失败!\n预期: {expected_md5}\n实际: {actual_md5}\n\n")
            messagebox.showerror("失败", "文件MD5验证失败!")
    
    def save_md5(self):
        """保存MD5值到文件"""
        file_path = self.file_path.get()
        if not file_path or not os.path.isfile(file_path):
            messagebox.showerror("错误", "请选择有效的文件路径")
            return
        
        md5_value = self.calculate_md5(file_path)
        if md5_value is None:
            return
        
        md5_file = file_path + '.md5'
        try:
            with open(md5_file, 'w') as f:
                f.write(f"{md5_value} *{os.path.basename(file_path)}")
            
            self.result_text.insert(tk.END, f"MD5值已保存到: {md5_file}\n\n")
            messagebox.showinfo("成功", f"MD5值已保存到:\n{md5_file}")
        except IOError as e:
            messagebox.showerror("错误", f"无法写入文件: {str(e)}")
    
    def clear_all(self):
        """清除所有内容"""
        self.file_path.set("")
        self.expected_md5.set("")
        self.result_text.delete(1.0, tk.END)
        self.status_var.set("就绪")

if __name__ == '__main__':
    root = tk.Tk()
    app = MD5CheckerApp(root)
    root.mainloop()